/**************************************************************************************************
 * @file        os_rw_lock.h
 * @brief       读写锁
 * @details
 *
 * @author      taoist.luo
 * @version     1.0.0
 * @date        2024-09
 *
 * Copyright (c) 2024 taoist.luo (https://gitee.com/harmonyluo(https://blog.csdn.net/harmonyluo))
 *
 * MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *************************************************************************************************/
#pragma once

#include <chrono>
#include <condition_variable>
#include <mutex>
// ‌std::chrono::hours ‌：小时
// ‌std::chrono::minutes‌ ：分钟
// ‌std::chrono::seconds‌ ：秒
// ‌std::chrono::milliseconds‌ ：毫秒
// ‌std::chrono::microseconds‌ ：微秒
// ‌std::chrono::nanoseconds‌ ：纳秒
// （1）当有写者持有锁时，所有读者和写者都必须等待。
// （2）当有写者等待时，新读者必须等待（写者优先）。
// （3）写者获取锁的条件是：没有读者，且没有其他写者
// （4）多个读者可以同时持有读锁（读读不互斥）
// （5）同一线程可以递归获取读锁（可重入读）
// （6）写锁是独占的，同一时间只能有一个写者
// （7）防止写者饥饿（公平性）
// （8）防止死锁

#include <chrono>
#include <condition_variable>
#include <map>
#include <mutex>
#include <thread>

class os_rw_lock
{
public:
    os_rw_lock();
    ~os_rw_lock() = default;

    // 禁止拷贝
    os_rw_lock(const os_rw_lock&) = delete;
    os_rw_lock& operator=(const os_rw_lock&) = delete;

    // 读锁操作
    void read_lock();
    bool try_read_lock();
    void read_unlock();

    // 写锁操作
    void write_lock();
    bool try_write_lock();
    void write_unlock();

    // 超时锁定操作
    template <typename Rep, typename Period>
    bool try_read_lock_for(const std::chrono::duration<Rep, Period>& timeout);

    template <typename Rep, typename Period>
    bool try_write_lock_for(const std::chrono::duration<Rep, Period>& timeout);

    template <typename Clock, typename Duration>
    bool try_read_lock_until(const std::chrono::time_point<Clock, Duration>& timeout_time);

    template <typename Clock, typename Duration>
    bool try_write_lock_until(const std::chrono::time_point<Clock, Duration>& timeout_time);

    // 状态查询
    int get_reader_count() const;
    int get_writer_count() const;
    int get_waiting_writers_count() const;
    bool is_write_privilege_granted() const;
    bool is_current_reader() const;
    bool is_current_writer() const;

    // RAII 包装器
    class read_guard
    {
    public:
        explicit read_guard(os_rw_lock& lock);
        ~read_guard();

        read_guard(const read_guard&) = delete;
        read_guard& operator=(const read_guard&) = delete;

    private:
        os_rw_lock& m_rw_lock;
    };

    class write_guard
    {
    public:
        explicit write_guard(os_rw_lock& lock);
        ~write_guard();

        write_guard(const write_guard&) = delete;
        write_guard& operator=(const write_guard&) = delete;

    private:
        os_rw_lock& m_rw_lock;
    };

private:
    // 核心状态变量
    mutable std::mutex m_mutex;
    std::condition_variable m_reader_cv;
    std::condition_variable m_writer_cv;

    int m_reader_count;
    int m_writer_count;
    int m_waiting_writers;
    bool m_write_privilege;

    // 可重入支持
    std::map<std::thread::id, int> m_reader_recursion;
    std::thread::id m_current_writer;

    // 内部辅助方法
    bool can_acquire_read_lock() const;
    bool can_acquire_write_lock() const;
    void acquire_read_lock_internal(std::thread::id thread_id);
    void acquire_write_lock_internal(std::thread::id thread_id);
    void release_read_lock_internal();
    void release_write_lock_internal();
};

// 模板方法必须在头文件中定义
template <typename Rep, typename Period>
bool os_rw_lock::try_read_lock_for(const std::chrono::duration<Rep, Period>& timeout)
{
    std::unique_lock<std::mutex> lock(m_mutex);
    std::thread::id this_id = std::this_thread::get_id();

    // 检查重入
    auto it = m_reader_recursion.find(this_id);
    if (it != m_reader_recursion.end()) {
        it->second++;
        return true;
    }

    // 使用条件变量避免虚假唤醒
    if (m_reader_cv.wait_for(lock, timeout, [this]() {
            return can_acquire_read_lock();
        }))
    {
        acquire_read_lock_internal(this_id);
        return true;
    }
    return false;
}

template <typename Rep, typename Period>
bool os_rw_lock::try_write_lock_for(const std::chrono::duration<Rep, Period>& timeout)
{
    std::unique_lock<std::mutex> lock(m_mutex);
    std::thread::id this_id = std::this_thread::get_id();

    // 检查重入
    if (m_writer_count > 0 && m_current_writer == this_id) {
        m_writer_count++;
        return true;
    }

    m_waiting_writers++;

    // 使用条件变量避免虚假唤醒
    if (m_writer_cv.wait_for(lock, timeout, [this]() {
            return can_acquire_write_lock();
        }))
    {
        m_waiting_writers--;
        acquire_write_lock_internal(this_id);
        return true;
    }

    m_waiting_writers--;
    return false;
}

template <typename Clock, typename Duration>
bool os_rw_lock::try_read_lock_until(const std::chrono::time_point<Clock, Duration>& timeout_time)
{
    std::unique_lock<std::mutex> lock(m_mutex);
    std::thread::id this_id = std::this_thread::get_id();

    // 检查重入
    auto it = m_reader_recursion.find(this_id);
    if (it != m_reader_recursion.end()) {
        it->second++;
        return true;
    }

    // 使用条件变量避免虚假唤醒
    if (m_reader_cv.wait_until(lock, timeout_time, [this]() {
            return can_acquire_read_lock();
        }))
    {
        acquire_read_lock_internal(this_id);
        return true;
    }

    return false;
}

template <typename Clock, typename Duration>
bool os_rw_lock::try_write_lock_until(const std::chrono::time_point<Clock, Duration>& timeout_time)
{
    std::unique_lock<std::mutex> lock(m_mutex);
    std::thread::id this_id = std::this_thread::get_id();

    // 检查重入
    if (m_writer_count > 0 && m_current_writer == this_id) {
        m_writer_count++;
        return true;
    }

    m_waiting_writers++;

    // 使用条件变量避免虚假唤醒
    if (m_writer_cv.wait_until(lock, timeout_time, [this]() {
            return can_acquire_write_lock();
        }))
    {
        m_waiting_writers--;
        acquire_write_lock_internal(this_id);
        return true;
    }

    m_waiting_writers--;
    return false;
}